Repository navigation
🤖 docs: bug-bash-sandbox-check launches three jobs, not one - #6095
Merged
Merged
Conversation
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
This was referenced Oct 11, 2026
yermakoffivan
pushed a commit
to yermakoffivan/mux
that referenced
this pull request
Oct 11, 2026
Refs coder#6071. Depends on coder#6080 and coder#6087 (both merged): this branch is rebased onto them. ## Summary This PR updates zod from 4.4.3 to 4.6.5 and raises the `package.json` floor to `^4.6.5`. zod 4.5 and 4.6 build schemas much faster. Most of the first-load `main-*.js` task in `xum server` is the evaluation of the entry module graph, and about half of that is zod schema construction. The PR has six commits: 1. `perf: update zod to 4.6.5`: `package.json` and `bun.lock`. The lockfile keeps one root zod copy. The nested 4.6.1 copies under the MCP SDK packages are gone. 2. `fix: keep tuple result types exact in code_execution declarations`: one line in `src/node/services/ptc/typeGenerator.ts`, plus a regression test. Without it, the bump turns the `attach_file` result `value` into `never[]` in the code_execution type declarations. 3. `tests: pin persisted-data parsing across the zod 4.6 changes`: two behavioral tests for persisted data that the new parse rules touch. 4. `fix: fail history rows with a strict-object __proto__ key, as zod 4.5 does`: one line in `src/node/services/historyMessageEvidence.ts`. That streaming readability check copies zod's strict-object rules by hand, and it still let an own `__proto__` key through. The existing differential test `validates actual workflow variants, strict fields, and unbounded JSON values` failed on the bump without it. 5. `fix: keep a code-point-safe prefix in streamed history evidence`: the same check kept `maxLength + 1` UTF-16 units of each string, but zod 4.6 counts string bounds in code points. A description of 1,025 emoji was cut to about 512 code points and looked readable. It now keeps `2 * (maxLength + 1)` units, and a differential test compares it with the real parse at 1,024 and 1,025 code points for ASCII, emoji and mixed input (Codex finding, round 1). 6. `fix: keep minute-precision workflow timestamps readable under zod 4.6`: `IsoDateTimeSchema` in `orpc/schemas/workflow.ts` and `attemptDeadlineAt` in `types/evaluation.ts` now use zod's documented union of both precisions (`offset: true`, with and without `precision: -1`). Without it, a `run.json` with a zoned timestamp without seconds became unreadable, and `createRunIfAbsent` then treated a deterministic child run as half-created and deleted its directory (Codex finding, round 2). Writers still use `toISOString()`. ## Performance **Local Lighthouse gate: pass.** Head's desktop eval median is 86.5 ms, against 160.05 ms on base: 73.6 ms lower (the gate needs at least 50 ms). Every head run stays under the bounds: desktop at most 88.1 ms observed and 88 ms reported (bound 120 ms), mobile at most 363 ms reported (bound 480 ms). No run was disqualified. **D2 Desktop Cold Start: pass.** On the current head (rebased onto coder#6087, no code changes): [run 38086092991](https://github.com/coder/xum/actions/runs/38086092991), base `cd6aa8c7c6` (merge-base), head `21fc89cbee`, 20 pairs. Mean change -5.17%, one-sided 95% upper bound -4.48% (gate: at most +5%), half-width 0.69%. Median `markMs`: base 765.2, head 724.0. Earlier heads: [run 38083040151](https://github.com/coder/xum/actions/runs/38083040151) on `b876091e48` (mean -4.90%, upper bound -4.17%), [run 38080882296](https://github.com/coder/xum/actions/runs/38080882296) on `c8262fd925` (mean -4.79%, upper bound -4.24%) and [run 38073922951](https://github.com/coder/xum/actions/runs/38073922951) on `368e36b5a2` (mean -6.14%, upper bound -4.66%). **Lighthouse auditor branch run (information, not a gate):** head `21fc89cbee` merged with main `0390895cc2`, compared with the auditor's main `cd6aa8c7c6` cycle. `0390895cc2` adds only coder#6082 (bug-bash test tooling, docs and one generated skill-content file). Lighthouse 13.5.0, 3 runs per page and form factor, all 12 head runs host-qualified. The host load average was higher than in earlier runs (13-24 on 32 CPUs, both sides). All 12 head runs are under the bounds. On main, 11 of 12 runs are over (seeded mobile run 1 was 449 ms). | Page and form factor | Longest `main-*.js` task, head (ms) | Same, main (ms) | TBT, head vs main | |---|---|---|---| | First-run, mobile | 354, 320, 323 | 750, 601, 578 | 270 vs 760 ms | | First-run, desktop | 82, 81, 90 | 188, 178, 141 | 30 vs 130 ms | | Seeded, mobile | 339, 329, 306 | 449, 556, 667 | 550 vs 710 ms | | Seeded, desktop | 80, 87, 78 | 160, 140, 140 | 60 vs 130 ms | LCP is unchanged. Served first-load JS grows by 7,496 bytes (brotli), almost all in the `API-*.js` chunk (+7,269), the same as on earlier heads. The auditor attributes this to zod 4.6.5 itself. Neither of us verified that. <details> <summary>Lighthouse method and every run</summary> Setup: Lighthouse 13.5.0 against `xum server --no-auth --host 127.0.0.1` under `env -i`, with a fresh `XUM_ROOT` and `HOME` and `XUM_DISABLE_TELEMETRY=1`, on the first-run page. Base and head ran as two servers at the same time, alternating run by run (8 desktop and 4 mobile each). Every build and measurement block ran under the shared host lock. "obsEval" is the `main-*.js` evaluation task in the trace. "LH main" is the task length Lighthouse reports (mobile is simulated with 4x CPU throttling). I wrote the host qualification rule before any candidate run. A run is disqualified only by host data: Lighthouse failed, the auditor's Chrome overlapped, CPU pressure (`/proc/pressure/cpu` some avg10) above 10, or a load average above 16 (32 CPUs). The retry budget was one extra interleaved pair per preset. It was not needed. A/A on base (two servers, same build): 0 of 24 runs disqualified. Desktop medians 155.8 and 155.15 ms (limit: within 10 ms). Mobile observed medians 154.65 and 157.45 ms (limit: within 40 ms). The host qualified. | side | preset | runs | disqualified | obsEval median | obsEval max | LH main median | LH main max | |---|---|---|---|---|---|---|---| | A1 | desktop | 8 | 0 | 155.8 | 162.4 | 155.5 | 162.0 | | A1 | mobile | 4 | 0 | 154.64999999999998 | 156.3 | 619.0 | 625.0 | | A2 | desktop | 8 | 0 | 155.15 | 163.7 | 155.5 | 164.0 | | A2 | mobile | 4 | 0 | 157.45 | 166.3 | 630.0 | 665.0 | Candidate, base vs head: | side | preset | run | obsEval ms | LH main-*.js ms | layout in task | TBT ms | psi avg10 before/after | load before/after | qualified | |---|---|---|---|---|---|---|---|---|---| | base | desktop | 1 | 160.6 | 161.0 | 0 | 115.0 | 0.0/0.72 | 2.49/3.69 | yes | | head | desktop | 1 | 85.9 | 86.0 | 0 | 41.0 | 0.72/0.23 | 3.69/3.69 | yes | | base | desktop | 2 | 154.2 | 154.0 | 0 | 104.0 | 0.23/0.05 | 3.69/4.14 | yes | | head | desktop | 2 | 85.6 | 86.0 | 0 | 41.0 | 0.05/0.01 | 4.14/4.97 | yes | | base | desktop | 3 | 162.9 | 163.0 | 0 | 119.0 | 0.01/0.0 | 4.97/5.58 | yes | | head | desktop | 3 | 85.1 | 85.0 | 0 | 41.0 | 0.0/0.08 | 5.58/6.3 | yes | | base | desktop | 4 | 149.3 | 149.0 | 0 | 105.0 | 0.08/0.01 | 6.3/8.03 | yes | | head | desktop | 4 | 87.7 | 88.0 | 0 | 43.0 | 0.01/0.0 | 8.03/7.8 | yes | | base | desktop | 5 | 159.5 | 159.0 | 0 | 109.0 | 0.0/0.0 | 7.8/7.36 | yes | | head | desktop | 5 | 87.1 | 87.0 | 0 | 37.0 | 0.0/0.0 | 7.36/7.07 | yes | | base | desktop | 6 | 385.8 | 193.0 | 1 | 237.0 | 0.0/3.69 | 7.07/8.83 | yes | | head | desktop | 6 | 88.1 | 88.0 | 0 | 38.0 | 3.69/0.91 | 8.83/7.89 | yes | | base | desktop | 7 | 161.6 | 162.0 | 0 | 117.0 | 0.91/0.22 | 7.89/7.42 | yes | | head | desktop | 7 | 87.1 | 87.0 | 0 | 38.0 | 0.22/0.05 | 7.42/6.9 | yes | | base | desktop | 8 | 151.1 | 151.0 | 0 | 106.0 | 0.05/0.06 | 6.9/6.94 | yes | | head | desktop | 8 | 85.0 | 85.0 | 0 | 41.0 | 0.06/0.01 | 6.94/6.16 | yes | | base | mobile | 1 | 158.3 | 633.0 | 0 | 652.0 | 0.01/0.0 | 6.16/5.6 | yes | | head | mobile | 1 | 88.3 | 353.0 | 0 | 454.0 | 0.0/0.0 | 5.6/4.98 | yes | | base | mobile | 2 | 155.7 | 623.0 | 0 | 642.0 | 0.0/0.46 | 4.98/5.87 | yes | | head | mobile | 2 | 90.9 | 363.0 | 0 | 452.0 | 0.46/0.11 | 5.87/5.23 | yes | | base | mobile | 3 | 147.9 | 591.0 | 0 | 608.0 | 0.11/0.02 | 5.23/4.7 | yes | | head | mobile | 3 | 87.0 | 348.0 | 0 | 428.0 | 0.02/0.0 | 4.7/4.9 | yes | | base | mobile | 4 | 155.3 | 621.0 | 0 | 638.0 | 0.0/0.18 | 4.9/4.55 | yes | | head | mobile | 4 | 87.0 | 348.0 | 0 | 432.0 | 0.18/0.04 | 4.55/4.05 | yes | | side | preset | runs | disqualified | obsEval median | obsEval max | LH main median | LH main max | |---|---|---|---|---|---|---|---| | base | desktop | 8 | 0 | 160.05 | 385.8 | 160.0 | 193.0 | | base | mobile | 4 | 0 | 155.5 | 158.3 | 622.0 | 633.0 | | head | desktop | 8 | 0 | 86.5 | 88.1 | 86.5 | 88.0 | | head | mobile | 4 | 0 | 87.65 | 90.9 | 350.5 | 363.0 | Base desktop run 6 had a host stall (observed 385.8 ms, a Layout inside the task, pressure 3.69 after the run). It is under the disqualification limits, so it stays in the record. It changes neither the base median nor any head result. </details> ## zod changelog audit (4.4.3 to 4.6.5) I read the release notes for 4.5.0 through 4.6.5. Each row is a behavior change, Xum's use of the affected API, and the disposition. | Change (zod PR) | Xum use | Disposition | |---|---|---| | `z.iso.datetime()` with a zone requires seconds (coder#6457) | `IsoDateTimeSchema` in `orpc/schemas/workflow.ts` (about 24 fields), `evaluation.ts` `attemptDeadlineAt`, `sessionTape.ts` `startedAt`. Every producer writes `toISOString()`. | Restored for workflow records (commit 6): `run.json`, journal lines and evaluation deadlines with minute precision stay readable, as on zod 4.4. `sessionTape.ts` `startedAt` keeps the new rule: only the recorder writes it, with `toISOString()`, and a rejected header only skips that tape. | | String `.min()`/`.max()`/`.length()` count code points (coder#6441) | 3 tool-definition string bounds (`.min(2)`, `.min(5)`), `refinement.ts` sha256 `.length(64)` (hex). | Accept. `.max()` only loosens. The `.min()` bounds apply to model text and are tiny. | | Record keys and intersections match TypeScript (coder#6412) | 2 intersections (`agents.get`, `agentSkills.get` inputs). Regex-keyed records in config. | Accept. The intersection inputs now fold into one object in JSON Schema (see the schema table). The refine on the left side still runs. | | `unrecognized_keys` no longer aborts the object (coder#2200) | About 296 `.strict()` objects, including tool inputs. | Accept. A strict input with an extra key and a bad value now reports both issues. Tool error text gets more complete. | | `__proto__` is always stripped, and `.strict()` reports an own `__proto__` key (coder#6386, coder#6221) | Strict tool inputs, lenient `userPreferences` records. `evaluation.ts` already rejects `__proto__`. | Accept. 10 strict tool inputs now reject an own `__proto__` key. Commit 4 makes `historyMessageEvidence` agree with the real parse. New config-loading test pins that lenient preferences drop the key without adopting a prototype. | | Stricter string formats: ipv6, ulid, httpUrl, emoji (#6442, coder#6095, coder#6035, coder#6532) | No use of these formats. | Not affected. | | JSON Schema: closed tuples emit `items: false` / `additionalItems: false` (coder#6194) | Tool result schemas, oRPC OpenAPI, code_execution types. | Fixed in commit 2. `json-schema-to-typescript` reads only draft-7 tuples, so result types now use `target: "draft-7"`. | | JSON Schema: chained checks fold as a conjunction (coder#6554) | `session_history.offset_chars` (`.int().min(0)`). | Accept. The advertised `minimum` changes from `-MAX_SAFE_INTEGER` to `0`, which is what runtime already enforced. | | JSON Schema: nullable as a type array (`type: [X, "null"]`) instead of `anyOf` | 22 `.nullish()` tool inputs per route. | Accept. See "Provider routes". | | Error maps run on first read of `error` (coder#6519) | No `z.config` or error-map use. | Not affected. | | Numeric enum `.options` drop reverse mappings (coder#6542) | No `z.enum` over a TypeScript enum. | Not affected. | | base64 and email patterns rewritten (coder#6534) | No use. | Not affected. | | Metadata members (`.minLength` and others) become prototype getters (coder#6554). Methods live on the prototype (4.5). | `src/cli/proxifyOrpc.ts` clones a schema with a spread. | Accept. `proxifyOrpc` tests pass, and the CLI help comparison over 357 commands shows only help-text changes. | | `catch` and `prefault` fixes (coder#6192, coder#6440, coder#6587) | `lenient()` in `userPreferences.ts`, `.optional().catch(undefined)` in `message.ts` and `telemetry.ts`. | Accept. A differential parse on base and head was unchanged on the tested corpus: 44,596 real `chat.jsonl` rows, real config files and damaged variants of them. | ## Provider-facing schema comparison I dumped every tool definition through the real provider conversion paths (`getToolsForModel` and AI SDK `prepareTools` for OpenAI Responses, OpenAI Chat, Anthropic and Google), the oRPC OpenAPI document, and the code_execution type declarations, on base (4.4.3) and head (4.6.5). After I normalize `anyOf: [{type: X}, {type: "null"}]` to `type: [X, "null"]`, these differences remain: | Surface | Difference | Count | Cause | Evidence | |---|---|---|---|---| | Tool inputs, every route | nullable `anyOf` becomes a type array | 22 fields | zod JSON Schema output | live calls below | | Tool inputs, every route | `session_history.offset_chars` `minimum` -9007199254740991 becomes 0 | 1 field (per route) | coder#6554 | runtime already enforced 0 | | Tool result schemas | closed tuples gain `minItems`, `maxItems`, `additionalItems: false` | 6 | coder#6194 | commit 2 test | | oRPC OpenAPI | workflow datetime fields become `anyOf` of two strings: the seconds form (with `format: date-time`) and a minutes-only pattern | 3,072 | commit 6 (zod's union for coder#6457) | commit 6 tests | | oRPC OpenAPI | closed tuples gain `items: false` | 18 | coder#6194 | none needed (documentation only) | | oRPC OpenAPI | workflow descriptor `required` gains `scope` | 12 | `z.preprocess` now reports required | runtime already rejected a missing `scope` | | oRPC OpenAPI | `agents.get` and `agentSkills.get` `allOf` fold into one object | 2 | coder#6412 | the `xum api agents get --help` output now lists real flags instead of `--input [json]` | | code_execution types | `attach_file` result `value: unknown[]` becomes the exact tuple union | 1 | commit 2 | `ptc-types-diff` | ## Parse changes that touch persisted data 1. Minute-precision ISO datetimes with a zone (`2026-05-29T00:01Z`): zod 4.5 rejects them. Commit 6 keeps them readable in workflow records, as zod 4.4 did. The new `WorkflowRunStore.test.ts` cases check that a minute-precision `run.json` stays readable through `getRun` and `getRunStatusSnapshot`, that `createRunIfAbsent` keeps the existing run directory and journal, and that minute-precision journal lines stay. A `workflow.test.ts` case covers `attemptDeadlineAt`. All of them fail on `c8262fd925` (before commit 6) and pass with it. Xum writes every timestamp with `toISOString()`, and none of the 5,580 workflow files on this host had minute precision. 2. Strict tool inputs reject an own `__proto__` key. This only affects model tool calls, not persisted data. 3. A strict object with an unknown key and a bad value reports both issues. 4. String `.min()` and `.length()` count code points. The lenient `userPreferences` test passes on base and on head. It guards the `__proto__` and record changes: the loader keeps dropping only the bad entries, and no `__proto__` key becomes a prototype. ## Provider routes Tool input schemas now carry nullable fields as `type: [X, "null"]` (22 fields per route). I checked every route Xum supports: | Route | Adapter behavior | Result | |---|---|---| | Anthropic (direct) | pass-through | Live-verified. `claude-haiku-5-5` accepted the head tool set and returned a valid `session_history` call with nulls. | | OpenAI Responses | pass-through | Live-verified. `gpt-6-luna` accepted the head tool set and returned a valid call. | | Google Gemini | AI SDK sends `parametersJsonSchema` | Live-verified. `gemini-3.8-flash` (direct API) accepted all 39 functions, including the 22 type arrays, and returned a valid call. Google's structured-output docs accept `{"type": ["string","null"]}`, and `FunctionDeclaration.parametersJsonSchema` takes JSON Schema. | | xAI | `@ai-sdk/xai` drops `additionalProperties: false` only | Contract-verified, not live-verified. xAI's docs accept a type array for nullable fields. | | Ollama (built-in `ollama:` route) | coder#6080 rewrites every `type` array to the equivalent `anyOf` at the model boundary | Covered for old servers. Servers before v0.6.6 (ollama/ollama#9434) parse `type` only as a string. With zod 4.6.5 on this branch, the captured `/api/chat` body for the real `ollama:` tool set has 39 tools and 0 type arrays. The custom `openai-compatible` route (for example an old Ollama `/v1` endpoint, which decodes the same Go structs) gets the same rewrite since coder#6087. With zod 4.6.5 on this branch, a custom `openai-compatible` provider with the real tool set sends 39 tools and 0 type arrays. | | Amazon Bedrock | `@ai-sdk/amazon-bedrock` passes the schema through as `toolSpec.inputSchema.json` | **Not verified.** Bedrock documents the field as JSON Schema with no subset. Xum does not set `strict`. I have no Bedrock key, and I did not check how each Bedrock model family (Nova, Llama, Mistral) handles type arrays. | | OpenRouter | `@openrouter/ai-sdk-provider` passes the schema through | **Not verified.** OpenRouter does not document how it rewrites schemas for each upstream provider. I have no OpenRouter key. | Xum sets no tool-level `strict` on any of these routes, so no restricted strict-mode dialect applies. Live probe cost: one request per provider per side, about 34k input and 0.6k output tokens per side in total. I estimate under $0.10. I did not check invoices. ## Validation - On the current head `b876091e48`: `make static-check` and `make typecheck` pass (React Compiler line stays at 23/24). The workflow (store, runner, schema, evaluation), evidence, PTC, config and `providerModelFactory` suites pass: 1,301 tests in 36 files. - `make test` on `368e36b5a2` (before the code-point fix and the rebase): 24,793 pass, 3 fail. The 3 failures also fail on base `4a181066f4` on this host: 2 `taskGitPatchEngine` tests (the global `init.templateDir` points to a missing directory, so `.git/info` does not exist) and `BackupRepoCache > does not transfer 200 commits…` (5 s timeout under load). The one "unhandled error between tests" comes from that timed-out test. - Browser bundle: source maps list one zod root (`node_modules/zod`) on base and on head. - Regression proofs: - code_execution types: the new `typeValidator.test.ts` case fails on the bump without commit 2 (`Property 'type' does not exist on type 'never'`) and passes with it. The generated-type comparison base vs head differs only in the `attach_file` tuple. - Workflow timestamps: the `WorkflowRunStore.test.ts` and `workflow.test.ts` minute-precision cases fail on `c8262fd925` and pass with commit 6. With only the `workflow.ts` change reverted, the store cases fail. With only the `evaluation.ts` change reverted, the `attemptDeadlineAt` case fails. - `historyMessageEvidence.test.ts`: 10 of 10 pass on head with commits 4 and 5. With commit 4 on base (zod 4.4.3), the suite fails, so the fix belongs with the bump. Without commit 5, the new code-point test fails. - `xum <cmd> --help` for 357 CLI commands, base vs head: only help text changes. Nullable flags read "type: string or null", and `agents get` and `agent-skills get` list real flags instead of `--input [json]`. ## Dogfood Head build, `env -i` server on 127.0.0.1 with `XUM_MOCK_AI=1` (a fake Anthropic key on a dead loopback port, as the bug-bash seed does, because the composer needs a configured provider): 1. The onboarding dialog opens at "Xum Gateway (evaluation credits)", step 1 of 7. I stepped through all 7 steps and added a project in step 3. 2. The runtime picker lists Local, Worktree, SSH, Docker and Dev container. I created a Local workspace and sent a message. The mock reply arrived. 3. I stopped the server, appended an `agent_skill_list` card and a `workflow_run` card to the workspace's `chat.jsonl`, and restarted. Both cards render. The expanded skill card groups the skills by scope. 4. Reload: the transcript hydrates. The page and the console show no errors. Screenshots at 1440 px and 390 px and a video of the whole run are in a comment below. --- _Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high` • Cost: `$23.89`_ <!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high costs=23.89 -->
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
docs/AGENTS.mdsaidmake bug-bash-sandbox-check"launches one self-check job". It launches three sandbox jobs against the fake upstream: the self-check (tests/bugbash/sandbox/selfCheck.ts) and two export fixtures, a path traversal and an oversized frame (tests/bugbash/sandbox/selfCheckRun.ts). This PR fixes that sentence and regeneratesbuiltInSkillContent.generated.ts.Refs #5714 #5932. This was follow-up 6 of #6085.
Validation
make static-checkpasses. In the final evidence run on main (#5714 (comment)), the target ran three jobs and all three containers were removed.Generated with
xum• Model:anthropic:claude-opus-5-5• Thinking:high• Cost:$186.74